package com.dd.cloud.gateway.filter;

import com.dd.cloud.common.config.cookie.JwtProperties;
import com.dd.cloud.common.utils.AESUtil;
import com.dd.cloud.gateway.config.AesencryptProperties;
import com.dd.cloud.gateway.properties.FilterProperties;
import com.dd.cloud.gateway.util.PathUtil;
import com.google.common.base.Charsets;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * @Description: springcloud gateway全局过滤器 用于返回值加密, 优先级要高于参数加密的过滤器
 * @Author: czg
 * @Date: 2023/12/21 14:14
 * @Version: 1.0.0
 */
@EnableConfigurationProperties({JwtProperties.class, FilterProperties.class})
@Component
@Slf4j
public class ResultFilter implements GlobalFilter, Ordered {


    @Autowired
    private AesencryptProperties aesencryptProperties;



    /**
     * @param exchange:
     * @param chain:
     * @return Mono<Void>
     * @description gateway过滤器,返回值加密
     * @author czg
     * @date 2023/5/16 11:52
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse originalResponse = exchange.getResponse();
        boolean paramEncry = aesencryptProperties.getParamEncryptAble();
        originalResponse.beforeCommit(() -> Mono.fromRunnable(() -> {
            //添加响应头, AesParam为true 通知前端参数要加解密  false不加解密
            HttpHeaders headers = exchange.getResponse().getHeaders();
            if (headers.containsKey(PathUtil.ENCRYPT_HANDER)) {
                headers.set(PathUtil.ENCRYPT_HANDER, String.valueOf(paramEncry));
            } else {
                headers.add(PathUtil.ENCRYPT_HANDER, String.valueOf(paramEncry));
            }
            //设置这个消息头是为了自定义的消息头能被前端使用, 这个消息头值为自定义消息头的key
            if (headers.containsKey("Access-Control-Expose-Headers")) {
                headers.set("Access-Control-Expose-Headers", PathUtil.ENCRYPT_HANDER + "," +PathUtil.RESULT_HANDER);
            } else {
                headers.add("Access-Control-Expose-Headers", PathUtil.ENCRYPT_HANDER + "," +PathUtil.RESULT_HANDER);
            }

        }));
        // 加密请求头, 这个请求头的值为设定的值代表加解密, 其他值都不加解密
        String product = exchange.getRequest().getHeaders().getFirst(PathUtil.ENCRYPT_HANDER);
        log.debug("返回值是否要解密:{}", product);
        if (!"true".equals(product)) {
            return chain.filter(exchange.mutate().response(originalResponse).build());
        }
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        if (!aesencryptProperties.getResultEncryptAble()){
            return chain.filter(exchange.mutate().response(originalResponse).build());
        }
        //不加解密的请求
        if (PathUtil.handleExcludeUrl(exchange.getRequest(), aesencryptProperties.getExcludeUrls())) {
            return chain.filter(exchange.mutate().response(originalResponse).build());
        }
        if (!exchange.getRequest().getMethod().equals(HttpMethod.POST)){
            return chain.filter(exchange.mutate().response(originalResponse).build());
        }
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                        DataBuffer join = dataBufferFactory.join(dataBuffers);
                        byte[] content = new byte[join.readableByteCount()];
                        join.read(content);
                        DataBufferUtils.release(join);
                        // 流转为字符串
                        String responseData = null;
                        responseData = new String(content, Charsets.UTF_8);
                        log.debug("原返回值:{}", responseData);
                        if (Strings.isNotBlank(responseData)) {
                            try {
                                responseData = AESUtil.encrypt(responseData, aesencryptProperties.getKey());
                            } catch (Exception e) {
                                log.error("返回值加密失败");
                                e.printStackTrace();
                            }
                            log.debug("加密后的返回值: {}", responseData);
                        }
                        //URLEncoder.encode(source, "UTF-8");
                        byte[] uppedContent = responseData.getBytes(Charsets.UTF_8);
                        originalResponse.getHeaders().setContentLength(uppedContent.length);
                        originalResponse.getHeaders().add(PathUtil.RESULT_HANDER, aesencryptProperties.getResultEncryptAble().toString());
                        //设置这个消息头是为了自定义的消息头能被前端使用, 这个消息头值为自定义消息头的key
                        if (originalResponse.getHeaders().containsKey("Access-Control-Expose-Headers")) {
                            originalResponse.getHeaders().set("Access-Control-Expose-Headers", PathUtil.ENCRYPT_HANDER + "," +PathUtil.RESULT_HANDER);
                        } else {
                            originalResponse.getHeaders().add("Access-Control-Expose-Headers", PathUtil.ENCRYPT_HANDER + "," +PathUtil.RESULT_HANDER);
                        }
                        return bufferFactory.wrap(uppedContent);
                    }));
                } else {
                    log.error("获取响应体数据异常 ：" + getStatusCode());
                }

                return super.writeWith(body);
            }
        };
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

    /**
     * 权重，值越小优先级越高 这是返回值, 优先级要高于参数
     *
     * @return
     */
    @Override
    public int getOrder() {
        return -1;
    }


}
